/*
 * General main file outline for Towerhack
 */

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <stdbool.h>
#include "resources/resources.h"
#include "main.h"
#include "menu.h"
#include "lobby.h"
#include "game.h"
#include "config/setup.h"
#include "network.h"
#include "resources/filecheck.h"
#include "util/ipc.h"
#include "util/log.h"
#include "common/common_main.h"

ApplicationState main_state;

static void error_too_many_mode_arguments (const char* argv0)
{
	fprintf (stderr, "%s: Too many mode arguments.\n", argv0);
	log_print_usage_and_exit (argv0, EXIT_FAILURE);
}

static void parse_args (int argc, char* argv[], ApplicationState* start_state)
{
	enum {
		MODE_DEFAULT, // not (yet?) specified in command line args
		MODE_DAEMON,  // exec server only
		MODE_HOST,    // exec server and automatically connect local client
		MODE_CLIENT,  // automatically connect to remote server
	};
	
	// expected number of additional args by mode
	int mode_argc [] = { 0, 0, 1, 2 };
	
	int mode = MODE_DEFAULT;
	int autoready = false; // needs to be int because of struct option, see below
	
	struct option long_options[] =
	{
		{"daemon",    no_argument, &mode, 1},
		{"host",      no_argument, &mode, 2},
		{"client",    no_argument, &mode, 3},
		{"autoready", no_argument, &autoready, 1},
		{"help",      no_argument, NULL, '.'},
		{0, 0, 0, 0}
	};
	
	int opt_end = 0;
	int optc = 0;
	int loptind = 0;
		
	while (!opt_end) {
		optc = getopt_long (argc, argv, "dhcr", long_options, &loptind);

		switch (optc) {
		
		default:
		case -1:
			opt_end = 1;
			break;
		
		case 'd':
			if (mode == MODE_DEFAULT) {
				mode = MODE_DAEMON;
			} else {
				error_too_many_mode_arguments (argv[0]);
			}
			break;

		case 'h':
			if (mode == MODE_DEFAULT) {
				mode = MODE_HOST;
			} else {
				error_too_many_mode_arguments (argv[0]);
			}
			break;

		case 'c':
			if (mode == MODE_DEFAULT) {
				mode = MODE_CLIENT;
			} else {
				error_too_many_mode_arguments (argv[0]);
			}
			break;

		case 'r':
			if (autoready) {
				fprintf (stderr, "%s: Autoready is allowed just once.\n", argv[0]);
				log_print_usage_and_exit (argv[0], EXIT_FAILURE);
			} else {
				autoready = true;
			}
			break;

		case '.':
			if (argc == 2) {
				log_print_usage_and_exit (argv[0], EXIT_SUCCESS);
			} else {
				fprintf (stderr, "%s: Arguments not recognized.\n", argv[0]);
				log_print_usage_and_exit (argv[0], EXIT_FAILURE);
			}
			break;

		case '?': // Auto error message has been written by getopt
			log_print_usage_and_exit (argv[0], EXIT_FAILURE);

		}
	}
	
	int expected_argc = optind + mode_argc[mode];
	if (argc != expected_argc) {
		fprintf (stderr, "%s: Wrong number of arguments: expected %d, got %d.\n",
		         argv[0], argc, expected_argc);
		log_print_usage_and_exit (argv[0], EXIT_FAILURE);
	}
	
	if (autoready && (mode != MODE_CLIENT)) {
		fprintf (stderr, "%s: Autoready requires client-mode.\n", argv[0]);
		log_print_usage_and_exit (argv[0], EXIT_FAILURE);
	}
	
	switch (mode) {
	
	default:
	case MODE_DEFAULT:
		*start_state = STATE_MENU;
		break;
		
	case MODE_DAEMON:
		if (ipc_start_server ()) {
			exit(EXIT_SUCCESS);
		} else {
			fprintf (stderr, "%s: Failed to start server.\n", argv[0]);
			exit(EXIT_FAILURE);
		}
		break;
		
	case MODE_HOST:
		if (ipc_start_server ()) {
			lobby_configure ("localhost", argv[2], 0);
			*start_state = STATE_LOBBY;
		} else {
			fprintf (stderr, "%s: Failed to start server.\n", argv[0]);
			exit(EXIT_FAILURE);
		}
		break;
		
	case MODE_CLIENT:
		lobby_configure (argv[optind], argv[optind+1], autoready);
		*start_state = STATE_LOBBY;
		break;
		
	}
}

void update_state ()
{
	if (key[KEY_ESC]) {
		change_state (STATE_QUIT); // Also triggers cleanup
	} else {
		common_main_update_state ();
	}
}

void draw_state ()
{
	switch (main_state) {
		
	case STATE_QUIT: 
		break;
		
	case STATE_MENU:
		menu_draw ();
		break;
	
	case STATE_LOBBY:
		lobby_draw();
		break;
		
	case STATE_GAME:
		game_draw();
		break;
		
	default:
	case STATE_INITIAL:
		assert(!"cannot draw this state");
	
	}
}

void change_state (ApplicationState new_state)
{
	common_main_change_state (new_state);
}

void main_loop ()
{
	ticks = 0;
	while (main_state != STATE_QUIT) {
		while (ticks > 0) {
			update_state ();
			ticks--;
		}
		draw_state ();
		flip ();
		while (ticks <= 0) {
			rest (1);
		}
	}
}

int main (int argc, char* argv[])
{
	main_state = STATE_INITIAL;
	ApplicationState start_state = main_state;
	parse_args (argc, argv, &start_state);

	log_level = LOG_DEBUG;
	log_init (true, LOGFILE_CLIENT, true, true, true);

	filecheck_batch ("data/filelist.sh");

	load_allegro ();
	load_data (); /* from files */
	all_init ();
	change_state (start_state);
	
	main_loop ();

	cleanup ();
	log_info ("Thanks for 'playing' Towerhack");
	log_cleanup ();
	return EXIT_SUCCESS;
}
END_OF_MAIN()


#if TOWERHACK_DEBUG
#include "draw/map_display.h"

extern MapDisplay display;

void mouse_get_coords (int* buffer_x, int* buffer_y, int* map_x, int* map_y,
                       int* tile_x, int* tile_y, int* subtile)
{
	int l_buffer_x = mouse_x / SCALE_X;
	int l_buffer_y = mouse_y / SCALE_Y;
	int l_map_x = 0;
	int l_map_y = 0;
	
	if (main_state == STATE_GAME) {
		l_map_x = (l_buffer_x - display.offset_x);
		l_map_y = (l_buffer_y - display.offset_y);
		if (tile_x != NULL) *tile_x = l_map_x / TILE_SIZE;
		if (tile_y != NULL) *tile_y = l_map_y / TILE_SIZE;
		if (subtile != NULL) {
			*subtile = ((l_map_y / LTILE_SIZE) % SUB_RES) * SUB_RES +
		              (l_map_x / LTILE_SIZE) % SUB_RES;
		}
	} else {
		if (tile_x != NULL) *tile_x = 0;
		if (tile_y != NULL) *tile_y = 0;
		if (subtile != NULL) *subtile = 0;
	}
	
	if (map_x != NULL)    *map_x = l_map_x;
	if (buffer_x != NULL) *buffer_x = l_buffer_x;
	if (buffer_y != NULL) *buffer_y = l_buffer_y;
	if (map_y != NULL)    *map_y = l_map_y;
}
#endif
